// ========== CONFIGURACIÓN SUPABASE ==========
const SUPABASE_URL = 'https://fcjvdibwnatykbvsisxy.supabase.co';
const SUPABASE_ANON_KEY = 'sb_publishable_23rDcrD57vq4B1ocHWDClg_0tEbleYN';
const supabaseClient = supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
// ========== DATOS POR DEFECTO ==========
const DEFAULT_CONFIG = {
name: 'Súper Motor Import',
tagline: 'Piezas · Motos · Accesorios',
logoEmoji: '🏍',
color: '#E03A1E',
wa: '8499360197',
waMsg: '¡Hola! Me gustaría hacer el siguiente pedido:',
bannerTitle: 'Súper Gato Motors',
bannerSub: 'Potencia y confianza con el Dr. Neiser López',
bannerBadge: '🔥 OFERTAS ESPECIALES 🔥',
bannerImage: 'https://fcjvdibwnatykbvsisxy.supabase.co/storage/v1/object/public/banners/banner_1779979463581.jpg',
shipping: 5,
freeShip: 100,
pass: 'admin123',
socialSlogan: '🏍 Súper Gato Motors - Potencia y confianza con el Dr. Neiser López 🔥'
};
let S = {
config: {...DEFAULT_CONFIG},
welcomeCard: { icon:'🏍', title:'¡Bienvenido a Súper Motor Import!', message:'Encuentra las mejores motos, repuestos y accesorios al mejor precio.\n\n🚚 Envíos disponibles\n💬 Atención por WhatsApp\n✅ Productos de calidad', showOnLoad: true, lastUpdated: new Date().toISOString() },
categories: [{id:'c1',name:'Motos Completas',emoji:'🏍',desc:'Motos importadas'},{id:'c2',name:'Motor y Transmisión',emoji:'⚙️',desc:'Piezas de motor'},{id:'c3',name:'Frenos y Suspensión',emoji:'🔧',desc:'Sistema de frenos'},{id:'c4',name:'Electricidad',emoji:'⚡',desc:'Sistema eléctrico'},{id:'c5',name:'Carrocería',emoji:'🛡️',desc:'Piezas exteriores'},{id:'c6',name:'Accesorios',emoji:'🎽',desc:'Cascos, guantes'}],
products: [],
orders: [],
totalRevenue: 0
};
let cart = [];
let selectedCat = 'all';
let editingProductId = null;
let newProductImages = [];
let currentEditImages = [];
let welcomeCardShown = false;
// ========== VARIABLES PARA PAGINACIÓN ==========
let allProducts = [];
let currentPage = 0;
const PRODUCTS_PER_PAGE = 30; // Aumentamos para cargar más de una vez
let isLoadingMore = false;
let imageObserver = null;
// ========== UTILIDADES ==========
function showLoader() { document.getElementById('loader').classList.add('show'); }
function hideLoader() { document.getElementById('loader').classList.remove('show'); }
function toast(msg) { const el = document.getElementById('toast'); el.textContent = msg; el.classList.add('show'); setTimeout(() => el.classList.remove('show'), 2500); }
function sanitizeWhatsAppNumber(num) { return String(num || '').replace(/[^\d]/g, '').trim(); }
// ========== OPTIMIZACIÓN DE IMÁGENES ==========
function getOptimizedImageUrl(url) {
if (!url) return null;
return url.split('?')[0];
}
function getOptimizedModalImage(url) {
if (!url) return null;
return url.split('?')[0];
}
function getOptimizedBannerImage(url) {
if (!url) return null;
return url.split('?')[0];
}
// ========== FUNCIONES GLOBALES ==========
window.closeWelcomeCard = function() { document.getElementById('welcomeCard').classList.remove('open'); };
window.previewWelcomeCard = function() {
document.getElementById('welcome-icon').textContent = document.getElementById('welcome-icon-input').value || '🏍';
document.getElementById('welcome-title').textContent = document.getElementById('welcome-title-input').value || 'Vista previa';
document.getElementById('welcome-message').innerHTML = (document.getElementById('welcome-message-input').value || '').replace(/\n/g, '
');
document.getElementById('welcomeCard').classList.add('open');
};
window.closeDrawer = function() { document.getElementById('drawerBg').classList.remove('open'); };
window.openDrawer = function() { document.getElementById('drawerBg').classList.add('open'); window.renderCart(); };
window.closeModal = function() {
document.getElementById('modalBg').classList.remove('open');
document.getElementById('modal-content').innerHTML = '';
};
window.bgClose = function(e, id) { if(e.target === document.getElementById(id)) { if(id === 'drawerBg') window.closeDrawer(); else window.closeModal(); } };
window.addToCart = function(id) { addToCartFunction(id); };
window.quickWa = function(id) { quickWaFunction(id); };
window.openModal = function(id) { openModalFunction(id); };
window.selectCat = function(id) { selectedCat = id; window.renderCatScroller(); window.renderProducts(); };
window.openAdmin = function() { openAdminFunction(); };
window.closeAdmin = function() { closeAdminFunction(); };
window.showPanel = function(id) { showPanelFunction(id); };
window.saveWelcomeConfig = function() { saveWelcomeConfigFunction(); };
window.saveConfig = function() { saveConfigFunction(); };
window.addProduct = function() { addProductFunction(); };
window.updateProduct = function() { updateProductFunction(); };
window.deleteProduct = function(id) { deleteProductFunction(id); };
window.openEditProduct = function(id) { openEditProductFunction(id); };
window.closeEditModal = function() { closeEditModalFunction(); };
window.addCategory = function() { addCategoryFunction(); };
window.deleteCategory = function(id) { deleteCategoryFunction(id); };
window.setOrderStatus = function(id, status) { setOrderStatusFunction(id, status); };
window.deleteOrder = function(id) { deleteOrderFunction(id); };
window.clearOrders = function() { clearOrdersFunction(); };
window.exportOrders = function() { exportOrdersFunction(); };
window.renderCart = function() { renderCartFunction(); };
window.updateBadge = function() { updateBadgeFunction(); };
window.renderProducts = function() { renderProductsFunction(); };
window.renderCatScroller = function() { renderCatScrollerFunction(); };
window.applyTheme = function() { applyThemeFunction(); };
window.exportData = function() { exportDataFunction(); };
window.importData = function(evt) { importDataFunction(evt); };
window.resetAll = function() { resetAllFunction(); };
window.changeModalImage = function(direction) { changeModalImageFunction(direction); };
window.setModalImage = function(index) { setModalImageFunction(index); };
// ========== FUNCIONES DE SUBIDA DE IMÁGENES ==========
async function uploadImagesToSupabase(files) {
const uploadedUrls = [];
for (const file of files) {
const fileExt = file.name.split('.').pop();
const fileName = `${Date.now()}-${Math.random().toString(36).substring(2, 8)}.${fileExt}`;
const filePath = `product-images/${fileName}`;
try {
const { error: uploadError } = await supabaseClient.storage
.from('product-images')
.upload(filePath, file, {
cacheControl: '31536000, immutable',
contentType: file.type,
upsert: true
});
if (uploadError) throw uploadError;
const { data: urlData } = supabaseClient.storage
.from('product-images')
.getPublicUrl(filePath);
uploadedUrls.push(urlData.publicUrl);
} catch (error) {
console.error('Error subiendo imagen:', error);
toast(`Error subiendo ${file.name}: ${error.message}`);
}
}
return uploadedUrls;
}
async function uploadSingleImageToStorage(file) {
if (!file) return null;
const fileExt = file.name.split('.').pop();
const fileName = `${Date.now()}-${Math.random().toString(36).substring(2, 8)}.${fileExt}`;
const filePath = `product-images/${fileName}`;
try {
const { error: uploadError } = await supabaseClient.storage
.from('product-images')
.upload(filePath, file, {
cacheControl: '31536000, immutable',
contentType: file.type,
upsert: true
});
if (uploadError) throw uploadError;
const { data: urlData } = supabaseClient.storage
.from('product-images')
.getPublicUrl(filePath);
return urlData.publicUrl;
} catch (error) {
console.error('Error subiendo imagen:', error);
toast(`Error subiendo ${file.name}: ${error.message}`);
return null;
}
}
async function uploadBannerImage(file) {
if (!file) return null;
try {
showLoader();
const fileExt = file.name.split('.').pop();
const fileName = `banner_${Date.now()}.${fileExt}`;
const { error } = await supabaseClient.storage.from('banners').upload(fileName, file, {
cacheControl: '31536000, immutable',
contentType: file.type,
upsert: true
});
if (error) { console.error(error); hideLoader(); toast('❌ Error: ' + error.message); return null; }
const { data: urlData } = supabaseClient.storage.from('banners').getPublicUrl(fileName);
hideLoader();
return urlData.publicUrl;
} catch (err) { console.error(err); hideLoader(); toast('❌ Error al subir la imagen'); return null; }
}
// ========== FUNCIONES DE LA TIENDA ==========
function applyThemeFunction() {
const cfg = S.config;
const bannerElement = document.getElementById('store-banner');
if (bannerElement && cfg.bannerImage) {
const optimizedBanner = getOptimizedBannerImage(cfg.bannerImage);
bannerElement.style.backgroundImage = `linear-gradient(135deg, rgba(17,17,17,0.7), rgba(30,30,30,0.6)), url('${optimizedBanner}')`;
}
document.documentElement.style.setProperty('--red', cfg.color);
document.getElementById('store-brand-name').textContent = (cfg.name || 'Súper Motor Import').toUpperCase();
document.getElementById('store-tagline').textContent = cfg.tagline || '';
document.getElementById('store-logo-icon').textContent = cfg.logoEmoji || '🏍';
document.getElementById('banner-title').textContent = cfg.bannerTitle || '';
document.getElementById('banner-sub').textContent = cfg.bannerSub || '';
document.getElementById('banner-badge').textContent = cfg.bannerBadge || '';
}
function renderCatScrollerFunction() {
const el = document.getElementById('cat-scroller');
if(!el) return;
let html = `
Todos (${S.products.length})
`;
S.categories.forEach(c => {
const cnt = S.products.filter(p => p.cat === c.id).length;
if(cnt>0) html += `${c.emoji} ${c.name} (${cnt})
`;
});
el.innerHTML = html;
}
// ========== RENDERIZADO CON CARGA INSTANTÁNEA DE TODAS LAS IMÁGENES ==========
function renderProductsFunction() {
const grid = document.getElementById('products-grid');
if(!grid) return;
const q = (document.getElementById('search-inp')?.value || '').toLowerCase();
let prods = [...S.products];
if(selectedCat !== 'all') prods = prods.filter(p => p.cat === selectedCat);
if(q) prods = prods.filter(p => p.name.toLowerCase().includes(q));
allProducts = prods;
currentPage = 0;
grid.innerHTML = '';
isLoadingMore = false;
document.getElementById('sec-count-txt').textContent = `${allProducts.length} productos`;
if (allProducts.length === 0) {
grid.innerHTML = '🔍
No se encontraron productos
';
document.getElementById('loadMoreTrigger').style.display = 'none';
return;
}
renderProductsBatch();
setupInfiniteScroll();
// Cargar las imágenes del siguiente lote en segundo plano
setTimeout(() => preloadNextBatch(), 300);
}
function renderProductsBatch() {
const grid = document.getElementById('products-grid');
if(!grid) return;
const start = currentPage * PRODUCTS_PER_PAGE;
const end = Math.min(start + PRODUCTS_PER_PAGE, allProducts.length);
const batch = allProducts.slice(start, end);
if (batch.length === 0) {
document.getElementById('loadMoreTrigger').style.display = 'none';
return;
}
const isMobile = window.innerWidth < 600;
const batchHtml = batch.map(p => {
const cat = S.categories.find(c => c.id === p.cat);
const images = (p.images && p.images.length > 0) ? p.images : [];
const mainImg = images.length > 0 ? images[0] : null;
const cleanUrl = mainImg ? getOptimizedImageUrl(mainImg) : null;
// Usamos eager para que todas las imágenes se carguen inmediatamente
let mainImgHtml = '';
if (cleanUrl) {
// Para móvil usamos srcset con imágenes más pequeñas
const mobileWidth = isMobile ? 200 : 400;
mainImgHtml = `
`;
} else {
mainImgHtml = `${p.emoji||'📦'}
`;
}
let flagHtml = '';
if(p.status === 'new') flagHtml = 'Nuevo';
else if(p.status === 'sale') flagHtml = 'Oferta';
else if(p.status === 'hot') flagHtml = '🔥 Popular';
else if(p.status === 'out') flagHtml = 'Sin stock';
let carouselHtml = '';
if (images.length > 1) {
carouselHtml = ``;
images.slice(0, 4).forEach((img, idx) => {
const cleanImg = getOptimizedImageUrl(img);
carouselHtml += `

`;
});
carouselHtml += `
📸 ${images.length} `;
}
return `
${mainImgHtml}
${carouselHtml}
${flagHtml}
${cat ? cat.emoji+' '+cat.name : ''}
${p.name}
$${parseFloat(p.price).toFixed(2)}
${p.old_price > 0 ? `$${parseFloat(p.old_price).toFixed(2)}` : ''}
${p.stock > 0 ? '✅ '+p.stock+' disponibles' : '❌ Sin stock'}
`;
}).join('');
grid.innerHTML += batchHtml;
currentPage++;
const trigger = document.getElementById('loadMoreTrigger');
if (currentPage * PRODUCTS_PER_PAGE < allProducts.length) {
trigger.style.display = 'block';
trigger.innerHTML = '🔄 Desplázate para cargar más productos...';
} else {
trigger.style.display = 'none';
}
// Cargar imágenes en segundo plano con IntersectionObserver
observeImages();
}
// ========== INTERSECTION OBSERVER PARA PRECARGAR IMÁGENES ==========
function observeImages() {
if (imageObserver) imageObserver.disconnect();
imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
const src = img.getAttribute('data-src');
if (src) {
img.src = src;
img.removeAttribute('data-src');
}
imageObserver.unobserve(img);
}
});
}, { rootMargin: '200px' });
document.querySelectorAll('.prod-card img[data-src]').forEach(img => {
imageObserver.observe(img);
});
}
// ========== PRECARGAR SIGUIENTE LOTE EN SEGUNDO PLANO ==========
function preloadNextBatch() {
const nextStart = currentPage * PRODUCTS_PER_PAGE;
const nextBatch = allProducts.slice(nextStart, nextStart + PRODUCTS_PER_PAGE);
if (nextBatch.length === 0) return;
// Pre-cargar imágenes del siguiente lote en segundo plano
nextBatch.forEach(p => {
if (p.images && p.images.length > 0) {
const img = new Image();
img.src = getOptimizedImageUrl(p.images[0]);
}
});
}
function setupInfiniteScroll() {
if (window._scrollListener) {
window.removeEventListener('scroll', window._scrollListener);
}
const scrollHandler = () => {
if (isLoadingMore) return;
if (currentPage * PRODUCTS_PER_PAGE >= allProducts.length) return;
const trigger = document.getElementById('loadMoreTrigger');
if (!trigger) return;
const rect = trigger.getBoundingClientRect();
if (rect.top <= window.innerHeight + 100) {
isLoadingMore = true;
trigger.innerHTML = ' Cargando más productos...';
setTimeout(() => {
renderProductsBatch();
preloadNextBatch();
isLoadingMore = false;
}, 300);
}
};
window._scrollListener = scrollHandler;
window.addEventListener('scroll', scrollHandler);
}
// ========== FUNCIONES DEL CARRITO ==========
function addToCartFunction(id) {
const p = S.products.find(x => x.id === id);
if(!p || p.stock === 0) return;
const ex = cart.find(i => i.id === id);
if(ex) {
if(ex.qty < p.stock) { ex.qty++; toast('✓ Cantidad actualizada'); }
else { toast('⚠️ Stock máximo'); return; }
} else {
cart.push({id, name: p.name, price: parseFloat(p.price), qty: 1, emoji: p.emoji || '📦', img: (p.images && p.images.length > 0) ? p.images[0] : null});
toast('✓ Añadido al pedido');
}
updateBadgeFunction();
}
function removeFromCart(id) { cart = cart.filter(i => i.id !== id); updateBadgeFunction(); renderCartFunction(); }
function changeQty(id, d) { const item = cart.find(i => i.id === id); if(!item) return; item.qty += d; if(item.qty <= 0) { removeFromCart(id); return; } const prod = S.products.find(p => p.id === id); if(prod && item.qty > prod.stock) { item.qty = prod.stock; toast('⚠️ Stock máximo'); } renderCartFunction(); }
function cartTotal() { return cart.reduce((s,i) => s + i.price * i.qty, 0); }
function cartItems() { return cart.reduce((s,i) => s + i.qty, 0); }
function updateBadgeFunction() { const badge = document.getElementById('cart-badge'); if(badge) badge.textContent = cartItems(); }
function renderCartFunction() {
const body = document.getElementById('drawer-body');
const foot = document.getElementById('drawer-foot');
if(!cart.length) { body.innerHTML = '🛒
Tu pedido está vacío
'; foot.innerHTML = ''; return; }
body.innerHTML = cart.map(i => `${i.img ? `

` : `
${i.emoji}`}
${i.name}
$${i.price.toFixed(2)} c/u
${i.qty}
$${(i.price * i.qty).toFixed(2)}
`).join('');
const sub = cartTotal();
const freeFrom = S.config.freeShip || 100;
const ship = sub >= freeFrom ? 0 : (S.config.shipping || 5);
const total = sub + ship;
foot.innerHTML = `Subtotal$${sub.toFixed(2)}
Envío${ship === 0 ? 'GRATIS ✅' : '$'+ship.toFixed(2)}
${sub < freeFrom ? `
Envío gratis desde $${freeFrom}
` : ''}
TOTAL$${total.toFixed(2)}
`;
}
function sendOrderFunction() {
if(!cart.length) { toast('⚠️ El pedido está vacío'); return; }
const nombre = document.getElementById('cust-nombre')?.value.trim();
const dni = document.getElementById('cust-dni')?.value.trim();
const direccion = document.getElementById('cust-direccion')?.value.trim();
const telefono = document.getElementById('cust-telefono')?.value.trim();
if(!nombre || !dni || !direccion || !telefono) { toast('⚠️ Complete todos los datos del cliente'); return; }
const waNum = sanitizeWhatsAppNumber(S.config.wa);
if(!waNum || waNum.length < 8) { toast('⚠️ Configure WhatsApp en Admin'); window.openAdmin(); return; }
const sub = cartTotal(), ship = sub >= (S.config.freeShip || 100) ? 0 : (S.config.shipping || 5), total = sub + ship;
let msg = `📋 *NUEVO PEDIDO - SÚPER MOTOR IMPORT - DR. NEISER LOPEZ*\n\n👤 *DATOS DEL CLIENTE*\nNombre: ${nombre}\nIdentificación: ${dni}\nDirección: ${direccion}\nTeléfono: ${telefono}\n\n━━━━━━━━━━━━━━━━━━━\n🛒 *PRODUCTOS SOLICITADOS*\n`;
cart.forEach(i => { msg += `${i.emoji} *${i.name}*\n ${i.qty} x $${i.price.toFixed(2)} = *$${(i.qty * i.price).toFixed(2)}*\n`; });
msg += `━━━━━━━━━━━━━━━━━━━\n📦 Subtotal: $${sub.toFixed(2)}\n🚚 Envío: ${ship === 0 ? 'GRATIS' : '$'+ship.toFixed(2)}\n💰 *TOTAL: $${total.toFixed(2)}*\n\n📅 Fecha: ${new Date().toLocaleString()}\n¡Gracias por confiar en Súper Motor Import! 🏍`;
const order = { id: '#' + Math.floor(1000 + Math.random() * 9000), date: new Date().toLocaleString('es'), items: [...cart], subtotal: sub, shipping: ship, total: total, customer: `${nombre} (${dni}) - ${direccion} - Tel:${telefono}`, status: 'pending' };
S.orders.unshift(order);
S.totalRevenue = (S.totalRevenue || 0) + total;
localStorage.setItem('smi_orders', JSON.stringify(S.orders));
localStorage.setItem('smi_revenue', S.totalRevenue);
window.open(`https://api.whatsapp.com/send?phone=${waNum}&text=${encodeURIComponent(msg)}`, '_blank');
cart = [];
updateBadgeFunction();
window.closeDrawer();
toast('✅ Pedido enviado');
}
function quickWaFunction(id) {
const p = S.products.find(x => x.id === id);
if(!p) return;
const waNum = sanitizeWhatsAppNumber(S.config.wa);
if(!waNum) { toast('⚠️ Configure WhatsApp'); return; }
window.open(`https://api.whatsapp.com/send?phone=${waNum}&text=${encodeURIComponent(`¡Hola! 👋\n\nEstoy interesado en: *${p.name}*\nPrecio: $${parseFloat(p.price).toFixed(2)}\n\n¿Disponibilidad y envíos?`)}`, '_blank');
}
// ========== MODAL PRODUCTO ==========
let currentModalImages = [], currentModalIndex = 0, currentModalProduct = null;
function openModalFunction(id) {
const p = S.products.find(x => x.id === id);
if(!p) return;
currentModalProduct = p;
const cat = S.categories.find(c => c.id === p.cat);
currentModalImages = (p.images && p.images.length > 0) ? p.images : [];
currentModalIndex = 0;
renderModalContent(cat);
document.getElementById('modalBg').classList.add('open');
}
function renderModalContent(cat) {
const p = currentModalProduct;
const hasImages = currentModalImages.length > 0;
let carouselHtml = '';
if (hasImages) {
const optimizedModalImg = getOptimizedModalImage(currentModalImages[currentModalIndex]);
carouselHtml = `
${currentModalImages.map((_, idx) => `
`).join('')}
`;
} else {
carouselHtml = ``;
}
document.getElementById('modal-content').innerHTML = `${carouselHtml}${cat ? cat.emoji + ' ' + cat.name : ''}
${p.name}
${p.description || ''}
$${parseFloat(p.price).toFixed(2)}${p.old_price > 0 ? `$${parseFloat(p.old_price).toFixed(2)}` : ''}
${p.stock > 0 ? '✅ ' + p.stock + ' unidades disponibles' : '❌ Sin stock'}
`;
}
function changeModalImageFunction(direction) {
if(currentModalImages.length === 0) return;
currentModalIndex = (currentModalIndex + direction + currentModalImages.length) % currentModalImages.length;
const slideDiv = document.querySelector('#modal-content .carousel-slide');
if (slideDiv) {
const optimizedImg = getOptimizedModalImage(currentModalImages[currentModalIndex]);
slideDiv.innerHTML = `
`;
}
const dots = document.querySelectorAll('#modal-content .carousel-dot');
dots.forEach((dot, idx) => {
if (idx === currentModalIndex) dot.classList.add('active');
else dot.classList.remove('active');
});
}
function setModalImageFunction(index) {
if(currentModalImages.length === 0) return;
currentModalIndex = index;
const slideDiv = document.querySelector('#modal-content .carousel-slide');
if (slideDiv) {
const optimizedImg = getOptimizedModalImage(currentModalImages[currentModalIndex]);
slideDiv.innerHTML = `
`;
}
const dots = document.querySelectorAll('#modal-content .carousel-dot');
dots.forEach((dot, idx) => {
if (idx === currentModalIndex) dot.classList.add('active');
else dot.classList.remove('active');
});
}
// ========== FUNCIONES DE ADMIN ==========
async function loadProductsFromAPI(showWelcome = true) {
showLoader();
try {
const { data, error } = await supabaseClient.from('products').select('*').order('created_at', { ascending: false });
if (error) throw error;
S.products = data || [];
console.log(`✅ ${S.products.length} productos cargados`);
applyThemeFunction();
renderCatScrollerFunction();
renderProductsFunction();
if (showWelcome && !welcomeCardShown && S.welcomeCard.showOnLoad !== false) {
setTimeout(() => { document.getElementById('welcomeCard').classList.add('open'); welcomeCardShown = true; }, 500);
}
} catch (error) { console.error('❌ Error:', error); toast('Error al cargar productos'); } finally { hideLoader(); }
}
async function openAdminFunction() {
const pass = prompt('Contraseña:');
if(pass !== (S.config.pass || 'admin123')) { toast('❌ Incorrecta'); return; }
document.getElementById('storeView').style.display = 'none';
document.getElementById('adminView').style.display = 'block';
loadAdminConfigFunction();
loadWelcomeConfigFunction();
renderDashboardFunction();
renderProdManageFunction();
renderCatManageFunction();
renderAllOrdersFunction();
}
function closeAdminFunction() {
document.getElementById('adminView').style.display = 'none';
document.getElementById('storeView').style.display = 'block';
applyThemeFunction();
renderCatScrollerFunction();
renderProductsFunction();
}
function showPanelFunction(id) {
document.querySelectorAll('.admin-panel').forEach(p => p.classList.remove('on'));
document.querySelectorAll('.admin-nav-btn').forEach(b => b.classList.remove('on'));
document.getElementById(id).classList.add('on');
const btn = document.querySelector(`[data-panel="${id}"]`);
if(btn) btn.classList.add('on');
}
function loadWelcomeConfigFunction() {
const wc = S.welcomeCard;
document.getElementById('welcome-icon-input').value = wc.icon || '🏍';
document.getElementById('welcome-title-input').value = wc.title || '';
document.getElementById('welcome-message-input').value = wc.message || '';
document.getElementById('welcome-show-input').value = wc.showOnLoad !== false ? 'true' : 'false';
}
function saveWelcomeConfigFunction() {
S.welcomeCard = {
icon: document.getElementById('welcome-icon-input').value,
title: document.getElementById('welcome-title-input').value,
message: document.getElementById('welcome-message-input').value,
showOnLoad: document.getElementById('welcome-show-input').value === 'true',
lastUpdated: new Date().toISOString()
};
localStorage.setItem('smi_welcome', JSON.stringify(S.welcomeCard));
toast('✅ Tarjeta actualizada');
}
function renderProdManageFunction() {
const el = document.getElementById('prod-manage-list');
document.getElementById('prod-count-label').textContent = '(' + S.products.length + ')';
if(!S.products.length) { el.innerHTML = 'Sin productos
'; return; }
el.innerHTML = S.products.map(p => {
const cat = S.categories.find(c => c.id === p.cat);
const imgHtml = (p.images && p.images.length > 0) ? `
` : `${p.emoji || '📦'}`;
return `${imgHtml}
${p.name}
${cat ? cat.name : ''} · Stock: ${p.stock} · ${p.images?.length || 0} imágenes
$${parseFloat(p.price).toFixed(2)}
`;
}).join('');
const sel = document.getElementById('p-cat'); if(sel) sel.innerHTML = S.categories.map(c => ``).join('');
}
function renderGalleryPreview(containerId, images, onRemove) {
const container = document.getElementById(containerId);
if(!container) return;
if(!images || images.length === 0) { container.innerHTML = 'Sin imágenes
'; return; }
container.innerHTML = images.map((img, idx) => `
`).join('');
}
function updateNewGalleryPreview() {
const previewUrls = newProductImages.map(item => item.previewUrl);
renderGalleryPreview('p-gallery-preview', previewUrls, async (idx) => {
newProductImages.splice(idx, 1);
updateNewGalleryPreview();
});
}
document.getElementById('p-gallery-input')?.addEventListener('change', (e) => {
const files = Array.from(e.target.files);
files.forEach(file => {
const reader = new FileReader();
reader.onload = (event) => {
newProductImages.push({
file: file,
previewUrl: event.target.result
});
updateNewGalleryPreview();
};
reader.readAsDataURL(file);
});
e.target.value = '';
});
function updateEditGalleryPreview() { renderGalleryPreview('edit-gallery-preview', currentEditImages, (idx) => { currentEditImages.splice(idx, 1); updateEditGalleryPreview(); }); }
document.getElementById('edit-gallery-input')?.addEventListener('change', async (e) => {
const files = Array.from(e.target.files);
for(const file of files) {
const url = await uploadSingleImageToStorage(file);
if(url) currentEditImages.push(url);
}
updateEditGalleryPreview();
e.target.value = '';
});
function openEditProductFunction(id) {
const p = S.products.find(x => x.id === id);
if(!p) return;
editingProductId = id;
currentEditImages = [...(p.images || [])];
document.getElementById('edit-name').value = p.name;
document.getElementById('edit-cat').innerHTML = S.categories.map(c => ``).join('');
document.getElementById('edit-price').value = p.price;
document.getElementById('edit-old-price').value = p.old_price || 0;
document.getElementById('edit-stock').value = p.stock;
document.getElementById('edit-emoji').value = p.emoji || '📦';
document.getElementById('edit-desc').value = p.description || '';
document.getElementById('edit-status').value = p.status || 'active';
updateEditGalleryPreview();
document.getElementById('editModal').classList.add('open');
}
function closeEditModalFunction() { document.getElementById('editModal').classList.remove('open'); editingProductId = null; currentEditImages = []; }
async function addProductFunction() {
const name = document.getElementById('p-name').value.trim();
const catId = document.getElementById('p-cat').value;
const price = parseFloat(document.getElementById('p-price').value || 0);
const oldPrice = parseFloat(document.getElementById('p-old-price').value || 0);
const stock = parseInt(document.getElementById('p-stock').value || 0);
const emoji = document.getElementById('p-emoji').value || '📦';
const desc = document.getElementById('p-desc').value || '';
const status = document.getElementById('p-status').value;
if (!name) { toast('⚠️ Escribe un nombre'); return; }
const imageFiles = newProductImages.map(item => item.file).filter(f => f);
let imageUrls = [];
if (imageFiles.length > 0) {
toast('📤 Subiendo imágenes...');
imageUrls = await uploadImagesToSupabase(imageFiles);
}
const newId = 'p_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6);
const { error } = await supabaseClient.from('products').insert([{
id: newId, name, cat: catId, price, old_price: oldPrice, stock, emoji,
description: desc, status, images: imageUrls
}]);
if (error) { toast('❌ Error: ' + error.message); return; }
toast('✅ Producto agregado');
document.getElementById('p-name').value = '';
document.getElementById('p-price').value = '';
document.getElementById('p-old-price').value = '';
document.getElementById('p-stock').value = '1';
document.getElementById('p-emoji').value = '🔧';
document.getElementById('p-desc').value = '';
newProductImages = [];
updateNewGalleryPreview();
await loadProductsFromAPI(false);
renderProdManageFunction();
renderCatScrollerFunction();
}
async function updateProductFunction() {
if (!editingProductId) return;
const { error } = await supabaseClient.from('products').update({
name: document.getElementById('edit-name').value.trim(),
cat: document.getElementById('edit-cat').value,
price: parseFloat(document.getElementById('edit-price').value),
old_price: parseFloat(document.getElementById('edit-old-price').value) || 0,
stock: parseInt(document.getElementById('edit-stock').value) || 0,
emoji: document.getElementById('edit-emoji').value.trim() || '📦',
description: document.getElementById('edit-desc').value.trim(),
status: document.getElementById('edit-status').value,
images: currentEditImages
}).eq('id', editingProductId);
if (error) { toast('❌ Error: ' + error.message); return; }
toast('✅ Producto actualizado');
await loadProductsFromAPI(false);
renderProdManageFunction();
closeEditModalFunction();
}
async function deleteProductFunction(id) {
if (!confirm('¿Eliminar producto?')) return;
const { error } = await supabaseClient.from('products').delete().eq('id', id);
if (error) { toast('❌ Error: ' + error.message); return; }
toast('🗑️ Producto eliminado');
await loadProductsFromAPI(false);
renderProdManageFunction();
renderCatScrollerFunction();
}
function renderDashboardFunction() {
document.getElementById('dash-stats').innerHTML = `$${(S.totalRevenue || 0).toLocaleString('es', {maximumFractionDigits:2})}
Ventas
${S.orders.length}
Pedidos
${S.products.length}
Productos
${S.categories.length}
Categorías
`;
const ordEl = document.getElementById('dash-orders');
if(!S.orders.length) { ordEl.innerHTML = 'Sin pedidos
'; }
else { ordEl.innerHTML = S.orders.slice(0,5).map(o => `${o.status || 'Pendiente'} 👤 ${o.customer}
$${(o.total || 0).toFixed(2)}
`).join(''); }
}
function renderAllOrdersFunction() { const el = document.getElementById('all-orders'); if(!S.orders.length) { el.innerHTML = ''; } else { el.innerHTML = S.orders.map(o => `${o.status || 'Pendiente'} 👤 ${o.customer}
${o.items.map(i => `${i.emoji} ${i.name} x${i.qty}`).join(' · ')}
$${(o.total || 0).toFixed(2)}
`).join(''); } }
function setOrderStatusFunction(id, status) { const o = S.orders.find(x => x.id === id); if(o) { o.status = status; localStorage.setItem('smi_orders', JSON.stringify(S.orders)); renderAllOrdersFunction(); renderDashboardFunction(); toast('Pedido actualizado'); } }
function deleteOrderFunction(id) { S.orders = S.orders.filter(x => x.id !== id); localStorage.setItem('smi_orders', JSON.stringify(S.orders)); renderAllOrdersFunction(); renderDashboardFunction(); toast('Pedido eliminado'); }
function clearOrdersFunction() { if(confirm('¿Eliminar todos los pedidos?')) { S.orders = []; S.totalRevenue = 0; localStorage.setItem('smi_orders', JSON.stringify(S.orders)); localStorage.setItem('smi_revenue', S.totalRevenue); renderAllOrdersFunction(); renderDashboardFunction(); toast('Pedidos eliminados'); } }
function exportOrdersFunction() { const rows = [['ID', 'Fecha', 'Cliente', 'Productos', 'Total', 'Estado']]; S.orders.forEach(o => rows.push([o.id, o.date, o.customer, o.items.map(i => `${i.name} x${i.qty}`).join('; '), o.total, o.status])); const csv = rows.map(r => r.map(c => `"${c}"`).join(',')).join('\n'); const a = document.createElement('a'); a.href = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csv); a.download = 'pedidos.csv'; a.click(); toast('CSV exportado'); }
function renderCatManageFunction() { const el = document.getElementById('cat-manage-list'); if(!S.categories.length) { el.innerHTML = 'Sin categorías
'; } else { el.innerHTML = S.categories.map(c => `${c.emoji}
${c.name}
${S.products.filter(p => p.cat === c.id).length} productos
`).join(''); } }
function addCategoryFunction() { const name = document.getElementById('c-name').value.trim(); if(!name) { toast('⚠️ Ingrese nombre'); return; } S.categories.push({ id: 'c' + Date.now(), name, emoji: document.getElementById('c-emoji').value.trim() || '📂', desc: document.getElementById('c-desc').value.trim() }); localStorage.setItem('smi_categories', JSON.stringify(S.categories)); renderCatManageFunction(); renderCatScrollerFunction(); document.getElementById('c-name').value = ''; document.getElementById('c-emoji').value = '🏍'; document.getElementById('c-desc').value = ''; toast('✅ Categoría creada'); }
function deleteCategoryFunction(id) { const cnt = S.products.filter(p => p.cat === id).length; if(cnt > 0 && !confirm(`Tiene ${cnt} productos. ¿Eliminar?`)) return; S.categories = S.categories.filter(c => c.id !== id); localStorage.setItem('smi_categories', JSON.stringify(S.categories)); renderCatManageFunction(); renderCatScrollerFunction(); toast('Categoría eliminada'); }
function loadAdminConfigFunction() {
const cfg = S.config;
document.getElementById('cfg-name').value = cfg.name || '';
document.getElementById('cfg-tagline').value = cfg.tagline || '';
document.getElementById('cfg-logo-emoji').value = cfg.logoEmoji || '🏍';
document.getElementById('cfg-color').value = cfg.color || '#E03A1E';
document.getElementById('cfg-wa').value = cfg.wa || '';
document.getElementById('cfg-wa-msg').value = cfg.waMsg || '';
document.getElementById('cfg-banner-title').value = cfg.bannerTitle || '';
document.getElementById('cfg-banner-sub').value = cfg.bannerSub || '';
document.getElementById('cfg-banner-badge').value = cfg.bannerBadge || '';
document.getElementById('cfg-ship').value = cfg.shipping || 5;
document.getElementById('cfg-free-ship').value = cfg.freeShip || 100;
document.getElementById('cfg-social-slogan').value = cfg.socialSlogan || '🏍 Súper Gato Motors - Potencia y confianza con el Dr. Neiser López 🔥';
}
async function saveConfigFunction() {
const newPass = document.getElementById('cfg-pass').value;
const cleanWa = sanitizeWhatsAppNumber(document.getElementById('cfg-wa').value);
const bannerFile = document.getElementById('cfg-banner-file').files[0];
let bannerImageUrl = S.config.bannerImage;
if(bannerFile) {
toast('📤 Subiendo imagen...');
const uploadedUrl = await uploadBannerImage(bannerFile);
if (uploadedUrl) bannerImageUrl = uploadedUrl;
}
S.config = {
...S.config,
name: document.getElementById('cfg-name').value,
tagline: document.getElementById('cfg-tagline').value,
logoEmoji: document.getElementById('cfg-logo-emoji').value,
color: document.getElementById('cfg-color').value,
wa: cleanWa,
waMsg: document.getElementById('cfg-wa-msg').value,
bannerTitle: document.getElementById('cfg-banner-title').value,
bannerSub: document.getElementById('cfg-banner-sub').value,
bannerBadge: document.getElementById('cfg-banner-badge').value,
bannerImage: bannerImageUrl,
shipping: parseFloat(document.getElementById('cfg-ship').value) || 5,
freeShip: parseFloat(document.getElementById('cfg-free-ship').value) || 100,
socialSlogan: document.getElementById('cfg-social-slogan').value
};
if(newPass) S.config.pass = newPass;
localStorage.setItem('smi_config', JSON.stringify(S.config));
await supabaseClient.from('site_config').upsert({ id: 1, ...S.config });
applyThemeFunction();
toast('✅ Configuración guardada');
}
function exportDataFunction() { const a = document.createElement('a'); a.href = 'data:application/json;charset=utf-8,' + encodeURIComponent(JSON.stringify({ config: S.config, welcomeCard: S.welcomeCard, categories: S.categories, products: S.products, orders: S.orders, totalRevenue: S.totalRevenue }, null, 2)); a.download = 'backup.json'; a.click(); toast('✅ Exportado'); }
function importDataFunction(evt) { const file = evt.target.files[0]; if(!file) return; const reader = new FileReader(); reader.onload = e => { try { const data = JSON.parse(e.target.result); if(data.config) S.config = data.config; if(data.welcomeCard) S.welcomeCard = data.welcomeCard; if(data.categories) S.categories = data.categories; if(data.products) S.products = data.products; if(data.orders) S.orders = data.orders; if(data.totalRevenue) S.totalRevenue = data.totalRevenue; localStorage.setItem('smi_config', JSON.stringify(S.config)); localStorage.setItem('smi_welcome', JSON.stringify(S.welcomeCard)); localStorage.setItem('smi_categories', JSON.stringify(S.categories)); localStorage.setItem('smi_products', JSON.stringify(S.products)); localStorage.setItem('smi_orders', JSON.stringify(S.orders)); localStorage.setItem('smi_revenue', S.totalRevenue); loadAdminConfigFunction(); loadWelcomeConfigFunction(); renderDashboardFunction(); renderProdManageFunction(); renderCatManageFunction(); renderAllOrdersFunction(); renderCatScrollerFunction(); renderProductsFunction(); applyThemeFunction(); toast('✅ Importado'); } catch(err) { toast('❌ Archivo inválido'); } }; reader.readAsText(file); }
function resetAllFunction() { if(confirm('¿RESETEAR TODO?')) { S = { config: { ...DEFAULT_CONFIG }, welcomeCard: { ...DEFAULT_CONFIG.welcomeCard }, categories: [...DEFAULT_CONFIG.categories], products: [], orders: [], totalRevenue: 0 }; localStorage.clear(); loadAdminConfigFunction(); loadWelcomeConfigFunction(); renderDashboardFunction(); renderProdManageFunction(); renderCatManageFunction(); renderAllOrdersFunction(); renderCatScrollerFunction(); renderProductsFunction(); applyThemeFunction(); toast('♻️ Resetado'); } }
// ========== EVENT LISTENERS ==========
document.getElementById('closeWelcomeBtn')?.addEventListener('click', () => window.closeWelcomeCard());
document.getElementById('welcomeBtn')?.addEventListener('click', () => window.closeWelcomeCard());
document.getElementById('cartBtn')?.addEventListener('click', () => window.openDrawer());
document.getElementById('drawerClose')?.addEventListener('click', () => window.closeDrawer());
document.getElementById('adminLink')?.addEventListener('click', () => window.openAdmin());
document.getElementById('backToStoreBtn')?.addEventListener('click', () => window.closeAdmin());
document.getElementById('modalClose')?.addEventListener('click', () => window.closeModal());
document.getElementById('editCancelBtn')?.addEventListener('click', () => window.closeEditModal());
document.getElementById('editUpdateBtn')?.addEventListener('click', () => window.updateProduct());
document.getElementById('addProductBtn')?.addEventListener('click', () => window.addProduct());
document.getElementById('addCategoryBtn')?.addEventListener('click', () => window.addCategory());
document.getElementById('saveWelcomeBtn')?.addEventListener('click', () => window.saveWelcomeConfig());
document.getElementById('previewWelcomeBtn')?.addEventListener('click', () => window.previewWelcomeCard());
document.getElementById('exportOrdersBtn')?.addEventListener('click', () => window.exportOrders());
document.getElementById('clearOrdersBtn')?.addEventListener('click', () => window.clearOrders());
document.getElementById('exportDataBtn')?.addEventListener('click', () => window.exportData());
document.getElementById('importFileInput')?.addEventListener('change', (e) => window.importData(e));
document.getElementById('resetAllBtn')?.addEventListener('click', () => window.resetAll());
document.getElementById('saveConfigBtn')?.addEventListener('click', () => window.saveConfig());
document.getElementById('saveConfigBtn2')?.addEventListener('click', () => window.saveConfig());
document.getElementById('saveConfigBtn3')?.addEventListener('click', () => window.saveConfig());
document.getElementById('saveConfigBtn4')?.addEventListener('click', () => window.saveConfig());
document.getElementById('editAddImgBtn')?.addEventListener('click', () => document.getElementById('edit-gallery-input').click());
document.getElementById('pAddImgBtn')?.addEventListener('click', () => document.getElementById('p-gallery-input').click());
document.getElementById('search-inp')?.addEventListener('input', () => renderProductsFunction());
document.querySelectorAll('.admin-nav-btn').forEach(btn => {
btn.addEventListener('click', () => window.showPanel(btn.getAttribute('data-panel')));
});
document.getElementById('drawerBg')?.addEventListener('click', (e) => window.bgClose(e, 'drawerBg'));
document.getElementById('modalBg')?.addEventListener('click', (e) => window.bgClose(e, 'modalBg'));
document.getElementById('editModal')?.addEventListener('click', (e) => { if(e.target === document.getElementById('editModal')) window.closeEditModal(); });
// ========== INICIALIZACIÓN ==========
async function init() {
const savedConfig = localStorage.getItem('smi_config');
if (savedConfig) S.config = { ...S.config, ...JSON.parse(savedConfig) };
const savedWelcome = localStorage.getItem('smi_welcome');
if (savedWelcome) S.welcomeCard = { ...S.welcomeCard, ...JSON.parse(savedWelcome) };
const savedCats = localStorage.getItem('smi_categories');
if (savedCats) S.categories = JSON.parse(savedCats);
const savedOrders = localStorage.getItem('smi_orders');
if (savedOrders) S.orders = JSON.parse(savedOrders);
const savedRevenue = localStorage.getItem('smi_revenue');
if (savedRevenue) S.totalRevenue = parseFloat(savedRevenue);
await loadProductsFromAPI(true);
applyThemeFunction();
console.log('🚀 Optimizado para móvil - Carga instantánea de todas las imágenes');
}
init();